import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Random;
import java.util.Vector;


public class Problem implements Parameters {
	private ArrayList<Integer> xArray;
	private ArrayList<Double> pArray;
	private int cost;
	private int id;
	private Random randGen;
	private int realExecTime;
	private double expetancy;
	private double resVal;
	
	//cTor
	public Problem(int identifyer,Random randGenerator){
		this.id=identifyer;
		this.setRandGen(randGenerator);
		initialize();
		setExpectancy();
		setResVal();
	}
	
	//cTor for known realValue problem
	public Problem(int identifyer,int c,int realVal){
		this.id=identifyer;
		cost=c;
		realExecTime=realVal;
		xArray=new ArrayList<Integer>();
		xArray.add(realVal-1);
		xArray.add(2);
		xArray.add(TOTAL_L-(realVal+1));
		
		pArray=new ArrayList<Double>();
		pArray.add(0.00001);
		pArray.add(0.99998);
		pArray.add(0.00001);
		setExpectancy();
		setResVal();
	}
	public double probabilitySmaller(int resVal){
		boolean exit_flag=true;
		int sumX=xArray.get(0),idX=0;
		//running until the interval is bigger than ResVal
		while(exit_flag){
			if(resVal>=TOTAL_L){
				return 0;
			}
			if(sumX>=resVal)
				exit_flag=false;
			else{
				idX++;
				sumX+=xArray.get(idX);
			}
		}
		//calculating inside the interval of ResVal
		double p=0;
		for(int i=0;i<idX;++i){
			p+=pArray.get(i);
		}
		double base=resVal-(sumX-xArray.get(idX));
		double height=pArray.get(idX)/xArray.get(idX);
		p=p+(base*height);
		
		return (1-p);
	}
	public void setResVal(){
		 Vector<Double> Rvec =new Vector<Double>();
		 resVal=TOTAL_L;
		 ArrayList<Integer>tmpX=new ArrayList<Integer>();
		 tmpX.add(0);
		 for(int i=0;i<xArray.size();i++){
			tmpX.add(tmpX.get(i)+xArray.get(i));
		 }
        for(int k=1; k<=pArray.size();k++){
            Rvec.add(calcOneResValue(pArray, tmpX, cost, k));
        }
         //check which rectangle Ri is in
        for(int i=0;i<Rvec.size();i++){
            if(Rvec.get(i)<=tmpX.get(i+1)&&(Rvec.get(i)>=tmpX.get(i)))
                resVal=Rvec.get(i);
        }
        
	}
	
	 public double calcOneResValue(ArrayList<Double> P, ArrayList<Integer> Xvec,double cost,int j){
         double R_value=0;
         double sideA=0;
         for(int k=1;k<j;k++){
             double Pi=0;
             for(int i=0;i<k-1;i++){
                 Pi=Pi+P.get(i);
             }
             sideA=sideA+Pi*(Xvec.get(k)-Xvec.get(k-1));
             double frac=(P.get(k-1)*(Math.pow(Xvec.get(k), 2)-2*Xvec.get(k)*Xvec.get(k-1)+Math.pow(Xvec.get(k-1), 2)));
             frac=frac/(2*(Xvec.get(k)-Xvec.get(k-1)));
             sideA=sideA+frac;
         }
         double pi=0;
         for(int i=0;i<j-1;i++)
             pi=pi+P.get(i);
         double C=(-cost*2*(Xvec.get(j)-Xvec.get(j-1))+(2*sideA*(Xvec.get(j)-Xvec.get(j-1)))+(-2*Xvec.get(j-1)*pi*(Xvec.get(j)-Xvec.get(j-1)))+(-P.get(j-1)*Math.pow(Xvec.get(j-1), 2))+(2*P.get(j-1)*Math.pow(Xvec.get(j-1), 2)));
         double A=P.get(j-1);
         double B=pi*2*(Xvec.get(j)-Xvec.get(j-1)) - (2*P.get(j-1)*Xvec.get(j-1));
         
         R_value=((-1*B)+ Math.sqrt(Math.pow(B, 2)-4*A*C))/(2*A);
         return R_value;
     }
	 
	 
	public void setExpectancy(){
		double tmp=0;
		int sumInterval=0;
		int newInterval=0;
		for(int i=0;i<pArray.size();i++){
			newInterval=xArray.get(i);
			tmp=tmp+(pArray.get(i)*(((newInterval)/2)+sumInterval));
			sumInterval+=newInterval;
		}
		setExpetancy(tmp);
	}
	public String toString(){
		StringBuilder sb=new StringBuilder();
		sb.append(id);
		sb.append(":");
		sb.append(cost);
		sb.append(":");
		sb.append(realExecTime);
		sb.append(":");
		sb.append(0);
		sb.append(":");
		sb.append(0);
		DecimalFormat currency = new DecimalFormat("#0.000000");
		for(int i=0;i<xArray.size();i++){
			sb.append(":");
			sb.append(xArray.get(i));
			sb.append(":");
			sb.append(currency.format(pArray.get(i)));
		}
		return sb.toString();
	}
	public void initialize(){
		//number of rects between 3 to 7
		int numOfRects=randGen.nextInt(MAX_NUM_OF_RECTS+1);
		while(numOfRects<3)
			numOfRects=randGen.nextInt(MAX_NUM_OF_RECTS+1);
		
		double sumL=0,sumP=0,t1,t2;
		cost=randGen.nextInt(MAX_QUERY_TIME)+1;	//1-MAX_QUERY_TIME
		ArrayList<Double> l=new ArrayList<Double>();
		ArrayList<Double> p=new ArrayList<Double>();
		for(int i=0;i<numOfRects;i++){
			t1=randGen.nextDouble();
			while(t1<=0)
				t1=randGen.nextDouble();
			t2=randGen.nextDouble();
			while(t2<=0)
				t2=randGen.nextDouble();
			sumL+=t1;
			sumP+=t2;
			l.add(t1);
			p.add(t2);
		}
		//nirmul
		int tmpSum=0;
		ArrayList<Integer> menurmalL=new ArrayList<Integer>();
		ArrayList<Double> menurmalP=new ArrayList<Double>();
		for(int i=0;i<numOfRects;i++){
			menurmalL.add((int)Math.rint(((l.get(i)/sumL)*TOTAL_L)+0.5));
			menurmalP.add((p.get(i)/sumP));
			tmpSum+=menurmalL.get(i);
		}
		//making sure the menurmalL will add up to TOTAL_L
		int k=0;
		if(tmpSum!=TOTAL_L){
			int val=menurmalL.get(k)+TOTAL_L-tmpSum;
			while(val<1){
				k++;
				val=menurmalL.get(k)+TOTAL_L-tmpSum;
			}
			menurmalL.set(k, val);
		}
		
		pArray=menurmalP;
		xArray=menurmalL;
		//configure the real executing time
		double tmpSumP=0,tmpSumL=0,prob,h;
		double randP=randGen.nextDouble();
		while(randP<=0)
			randP=randGen.nextDouble();
		//System.out.println("the rand is: "+randP);
		for(int i=0;i<numOfRects;i++){
			tmpSumP+=menurmalP.get(i);
			tmpSumL+=menurmalL.get(i);
			if(tmpSumP>=randP){
				h=menurmalP.get(i)/(double)menurmalL.get(i);
				if(i>0){
					prob=randP-(tmpSumP-menurmalP.get(i));
					tmpSumL=tmpSumL-menurmalL.get(i);
				}else{
					prob=randP;
					tmpSumL=0;
				}
				//System.out.println("tmpsumL: "+tmpSumL+" prob: "+prob+" h: "+h);
				realExecTime=(int) (tmpSumL+(prob/h));
				break;
			}
		}
	}
	public void setxArray(ArrayList<Integer> xArray) {
		this.xArray = xArray;
	}
	public ArrayList<Integer> getxArray() {
		return xArray;
	}
	public void setpArray(ArrayList<Double> pArray) {
		this.pArray = pArray;
	}
	public ArrayList<Double> getpArray() {
		return pArray;
	}
	public void setCost(int cost) {
		this.cost = cost;
	}
	public int getCost() {
		return cost;
	}
	public void setId(int id) {
		this.id = id;
	}
	public int getId() {
		return id;
	}
	public void setRandGen(Random randGen) {
		this.randGen = randGen;
	}
	public Random getRandGen() {
		return randGen;
	}
	public void setRealExecTime(int realExecTime) {
		this.realExecTime = realExecTime;
	}
	public double getRealExecTime() {
		return realExecTime;
	}

	public void setExpetancy(double expetancy) {
		this.expetancy = expetancy;
	}

	public double getExpetancy() {
		return expetancy;
	}

	public void setResVal(double resVal) {
		this.resVal = resVal;
	}

	public double getResVal() {
		return resVal;
	}
}
